#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include "List.h"

/***************************************************************************************************************/
/**********************************    struct Node & struct List    ********************************************/
/***************************************************************************************************************/
typedef struct Node_t
{
    void*           data;
    struct Node_t*  next;       /*  Pointer to next */
    struct Node_t*  prev;       /*  Pointer to prev */
}Node;

struct List_t
{
    Node head;
    Node tail;
};
/*******************************************************************************************************************/
/*****************************    Forward Decleration of internal Functions    *************************************/
/*******************************************************************************************************************/
static Node*   NodeNew(void* _data);                           /*  Creates new Node                                */
static void    NodeConnectNew(Node* _front, Node* _newNode);   /*  Function to connect new node between to exist   */
static void    NodeRemove(Node* _front);                       /*  Remove the node after fornt                     */
/*******************************************************************************************************************/
/*******************************************    Internal Functions    **********************************************/
/*******************************************************************************************************************/
Node* NodeNew(void* _data)                          /*  Creates new Node    */
{
    Node* node = (Node*)malloc(sizeof(Node));
    node->data = _data;

    return node;
}

void NodeConnectNew(Node* _front, Node* _newNode)
{
    _newNode->prev      = _front        ;           /*  The prev of new is front            */
    _newNode->next      = _front->next  ;           /*  the next of new is the front next   */
    _front->next->prev  = _newNode      ;
    _front->next        = _newNode      ;
}

void NodeRemove(Node* _front)
{
    Node* removed = _front->next;                   /*  Save the node to free                   */

    _front->next->next->prev = _front;              /*  The "After the removed" prev is front   */
    _front->next = _front->next->next;              /*  Also update the next of front           */
    free(removed);
}
/**************************************************************************************************************/

List* ListNew(void)
{
    List* list= (List*)malloc(sizeof(List));

    list->head.prev = 0;
    list->tail.next = 0;
    list->head.next = &(list->tail);
    list->tail.prev = &(list->head);
    list->head.data = 0;
    list->tail.data = 0;

    return list;
}


void ListDel(List* _list)
{
    assert(_list);

    while(_list->head.next != &(_list->tail))             /*  Next of head is no tail   */
    {
        ListPopH(_list);
    }
    free(_list);
}


int ListPushH(List* _list, void* _data)
{
    Node*   node;

    assert(_list);
    node = NodeNew(_data);
    if(!node)                   /*  Error on allocating */
    {
        return 1;
    }
    NodeConnectNew(&(_list->head), node);

    return 0;
}


int ListPushT(List* _list, void* _data)
{
    Node*   node;

    assert(_list);
    node = NodeNew(_data);
    if(!node)                   /*  Error on allocating */
    {
        return 1;
    }
    NodeConnectNew(_list->tail.prev, node);

    return 0;
}


void* ListHead(List* _list)
{
    assert(_list);
    return (_list->head.next)->data;
}


void* ListTail(List* _list)
{
    assert(_list);
    return (_list->tail.prev)->data;
}


void ListPopH(List* _list)
{
    assert(_list);
    assert(_list->head.next != &_list->tail);           /*  assert pop if head == tail                              */
    NodeRemove(&(_list->head));                         /*  Send the head as front                                  */
}


void ListPopT(List* _list)
{
    assert(_list);
    assert(_list->head.next != &_list->tail);           /*  assert pop if head == tail                              */
    NodeRemove(_list->tail.prev->prev);                 /*  Send as front the before removed                        */
}


size_t ListCount(List* _list)
{
    Node*       node    = &(_list->head);
    size_t      count   = 0             ;

    assert(_list);
    while(node->next != &(_list->tail) )
    {
        ++count;
        node = node->next;
    }
    return count;
}


int ListIsEmpty(List* _list)
{
    assert(_list);
    return _list->head.next == &(_list->tail);
}


void* ListFindIf(List* _list, int(*_isMatch)(void* _data, void* _param), void* _param)
{
    Node*   node;

    assert(_list);
    node    = &(_list->head);

    while(node->next != &(_list->tail))             /*  No the tail */
    {
        if(_isMatch(node->next->data, _param))
        {
            return node->next->data;
        }
        node = node->next;
    }
    return 0;                                       /*  Return NULL */
}


int ListPushHM(List* _list, size_t _elemSize, size_t _vecNOfE, void* _dataVector)
{
    int     i       ;
    int     res = 0 ;
    assert(_list);

    for(i = _vecNOfE * _elemSize - _elemSize; i >= 0; i-=_elemSize)
    {
        res = ListPushH(_list, (char*)_dataVector + i);
    }
    return res;
}


void ListPopHM(List* _list, size_t _vecNOfE)
{
    int     i;
    assert(_list);

    for(i = 0; i < _vecNOfE; ++i)
    {
        ListPopH(_list);
        assert(_list->head.next != &_list->tail);           /*  assert pop if head == tail                              */
    }
}


int ListPushTM(List* _list, size_t _elemSize, size_t _vecNOfE, void* _dataVector)
{
    int     i       ;
    int     res = 0 ;

    assert(_list);

    for(i = 0; i < _vecNOfE*_elemSize; i+=_elemSize)
    {
        res = ListPushT(_list, (char*)_dataVector + i);
    }
    return res;
}


void ListPopTM(List* _list, size_t _vecNOfE)
{
    int     i;
    assert(_list);

    for(i = 0; i < _vecNOfE; ++i)
    {
        ListPopT(_list);
        assert(_list->head.next != &_list->tail);           /*  assert pop if head == tail                              */
    }
}
